#include "gd32f4_usart.h"
static uint8_t RX_Buffer[RX_LENGTH + 1]; // +1 为了处理字符串结束符
static uint32_t RX_Count = 0;            // 标志第几个元素

// 函数声明
void DMA_TX_Config();
void DMA_RX_Config();
void USART_SendString(const char *str);

static void GPIO_Config() {
    // 配置USART0_TX引脚
    GPIO_Output_Af_Cfg(USART_TX_RCU, USART_TX_PORT, USART_TX_PIN, USART_TX_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE);
    // 配置USART0_RX引脚
    GPIO_Output_Af_Cfg(USART_RX_RCU, USART_RX_PORT, USART_RX_PIN, USART_RX_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE);
}

void USART_Config() {
    //---------------------------------------------串口配置---------------------------------------------
    // 使能时钟
    rcu_periph_clock_enable(USART_RCU);
    // 重置
    usart_deinit(USART_PORT);
    // 波特率(一定要配置)
    usart_baudrate_set(USART_PORT, USART_BAUDRATE);
    // 允许发送
    usart_transmit_config(USART_PORT, USART_TRANSMIT_ENABLE);
    // 允许接收
    usart_receive_config(USART_PORT, USART_RECEIVE_ENABLE);
    // 使能串口
    usart_enable(USART_PORT);

    //---------------------------------------------中断配置---------------------------------------------
    // 开启指定中断源，设置中断优先级
    nvic_irq_enable(USART_IRQN, USART_PRE_PRIORITY, USART_SUB_PRIORITY);

#if USART_DMA_RX_SW // 启用DMA接收
    /*...留空...*/
    /*...留空...*/
#else // 不启用DMA接收
    // RBNE, 中断标志位, 有数据可读，自动触发中断
    usart_interrupt_enable(USART_PORT, USART_INT_RBNE);
#endif

    // IDLE, 中断标志位, 空闲(没数据发和收)，自动触发中断
    usart_interrupt_enable(USART_PORT, USART_INT_IDLE);

//---------------------------------------------DMA配置---------------------------------------------
#if USART_DMA_TX_SW                                               // 串口启用DMA发送
    usart_dma_transmit_config(USART0, USART_TRANSMIT_DMA_ENABLE); // 使能DMA发送
#endif

#if USART_DMA_RX_SW                                             // 串口启用DMA接收
    usart_dma_receive_config(USART0, USART_RECEIVE_DMA_ENABLE); // 使能DMA接收
#endif
}

// DMA发送配置(内存到外设)
void DMA_TX_Config() {
    // 总线时钟挂载
    rcu_periph_clock_enable(DMA_TX_RCU);
    // 重置
    dma_deinit(DMA_TX_PERIPH, DMA_TX_CH);
    // 创建DMA参数结构体
    dma_single_data_parameter_struct DMA_InitStruct;
    dma_single_data_para_struct_init(&DMA_InitStruct);

    // 1.外设端设置
    DMA_InitStruct.periph_addr = (uint32_t)&USART_DATA(USART0); // 外设地址
    DMA_InitStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;    // 不增长

    // 2.内存端设置
    // 内存地址_动态配置
    DMA_InitStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 增长

    // 3.搬运元素的大小
    DMA_InitStruct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; // 一个数据8位

    // 4. 搬运个数——>动态配置

    // 5.方向
    DMA_InitStruct.direction = DMA_MEMORY_TO_PERIPH; // 内存到外设

    // 6. DMA优先级
    DMA_InitStruct.priority = DMA_PRIORITY_HIGH; // 高

    // 7. DMA初始化
    dma_single_data_mode_init(DMA_TX_PERIPH, DMA_TX_CH, &DMA_InitStruct);

    // 8. 子外设选择
    dma_channel_subperipheral_select(DMA_TX_PERIPH, DMA_TX_CH, DMA_TX_SUB_PERIPH);
}

// DMA接收配置(外设到内存)
void DMA_RX_Config() {
    // 总线时钟挂载
    rcu_periph_clock_enable(DMA_RX_RCU);
    // 重置
    dma_deinit(DMA_RX_PERIPH, DMA_RX_CH);

    // 创建DMA参数结构体
    dma_single_data_parameter_struct DMA_InitStruct;
    dma_single_data_para_struct_init(&DMA_InitStruct);

    // 1.外设端设置
    DMA_InitStruct.periph_addr = (uint32_t)&USART_DATA(USART0); // 外设地址
    DMA_InitStruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;    // 不增长

    // 2.内存端设置
    DMA_InitStruct.memory0_addr = (uint32_t)RX_Buffer;      // 内存地址
    DMA_InitStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 增长

    // 3.搬运元素的大小
    DMA_InitStruct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; // 一个数据8位

    // 4. 搬运个数——>动态配置
    DMA_InitStruct.number = RX_LENGTH;

    // 5.方向
    DMA_InitStruct.direction = DMA_PERIPH_TO_MEMORY; // 外设到内存

    // 6. DMA优先级
    DMA_InitStruct.priority = DMA_PRIORITY_HIGH; // 高优先级

    // 7. DMA初始化
    dma_single_data_mode_init(DMA_RX_PERIPH, DMA_RX_CH, &DMA_InitStruct);

    // 8. 子外设选择
    dma_channel_subperipheral_select(DMA_RX_PERIPH, DMA_RX_CH, DMA_RX_SUB_PERIPH);

    // 9. 启用DMA接收(数据随时都可能过来，所以配置完就让他工作)
    dma_channel_enable(DMA_RX_PERIPH, DMA_RX_CH);
}

// 发送1个字节
void USART_SendByte(uint8_t data) {
    #if USART_DMA_TX_SW // 使用DMA发送
    dma_memory_address_config(DMA_TX_PERIPH, DMA_TX_CH, DMA_MEMORY_0, (uint32_t)&data);
    dma_transfer_number_config(DMA_TX_PERIPH, DMA_TX_CH, 1); // 只发送1个字节
    dma_channel_enable(DMA_TX_PERIPH, DMA_TX_CH);
    while (RESET == dma_flag_get(DMA_TX_PERIPH, DMA_TX_CH, DMA_FLAG_FTF)); // 等待传输完成
    // 清除标志位
    dma_flag_clear(DMA_TX_PERIPH, DMA_TX_CH, DMA_FLAG_FTF);

    #else//不使用DMA发送
    usart_data_transmit(USART_PORT, data);
    // TBE标志位：发送数据缓冲区为空，自动置1，代表可以发下一个字节
    while (RESET == usart_flag_get(USART_PORT, USART_FLAG_TBE));
    #endif
}

// 发送字符串
void USART_SendString(const char *str) {
    // 检查字符串是否为空
    if (str == NULL)
        return;

    #if USART_DMA_TX_SW // 使用DMA发送
    dma_memory_address_config(DMA_TX_PERIPH, DMA_TX_CH, DMA_MEMORY_0, (uint32_t)str);
    dma_transfer_number_config(DMA_TX_PERIPH, DMA_TX_CH, strlen(str)); // 配置数据长度
    dma_channel_enable(DMA_TX_PERIPH, DMA_TX_CH);        // 启动DMA传输
    while (RESET == dma_flag_get(DMA_TX_PERIPH, DMA_TX_CH, DMA_FLAG_FTF)); // 等待传输完成
    // 清除标志位
    dma_flag_clear(DMA_TX_PERIPH, DMA_TX_CH, DMA_FLAG_FTF);

    #else//不使用DMA发送

    while (*str) {
        USART_SendByte((uint8_t)(*str));
        str++;
    }
    #endif
}

#if USART_PRINTF_SW
int fputc(int ch, FILE *f) { // 配置printf()
    USART_SendByte(ch);      // 发送字符
    return ch;               // 返回字符
}
#endif


void USART_Init(){
    GPIO_Config();           
#if USART_DMA_TX_SW // 如果使用DMA发送
    DMA_TX_Config();        
#endif
#if USART_DMA_RX_SW // 如果使用DMA接收
    DMA_RX_Config();        
#endif
    USART_Config();          
}









// 中断回调函数，名字不能乱写，在启动文件的.s中拷贝名字
void USART0_IRQHandler() {
#if USART_DMA_RX_SW // 使用DMA接收
    if (SET == usart_interrupt_flag_get(USART_PORT, USART_INT_FLAG_IDLE)){
        // 清除标志位, 可以让中断重复触发(接收一次达到清除标志位的作用)
        usart_data_receive(USART_PORT); //清除标志位
        //禁用DMA
        dma_channel_disable(DMA_RX_PERIPH, DMA_RX_CH);
        // 获取剩余没有搬运的个数
        uint32_t remain = dma_transfer_number_get(DMA_RX_PERIPH,DMA_RX_CH);
        // 计算实际接收的字节数
        RX_Count= RX_LENGTH - remain;
        RX_Buffer[RX_Count] = '\0'; // 字符串结束符
        #if USART_RX_CB_SW
        USART_RX_Recall(RX_Buffer, RX_Count);
        #endif
        RX_Count = 0; // 重置计数器
        // 清除标志位
		dma_flag_clear(DMA_RX_PERIPH,DMA_RX_CH, DMA_FLAG_FTF);
        // 启用DMA
		dma_channel_enable(DMA_RX_PERIPH,DMA_RX_CH);

    }

#else
    if (SET == usart_interrupt_flag_get(USART_PORT, USART_INT_FLAG_RBNE)) {
        // 清除标志位
        usart_interrupt_flag_clear(USART_PORT, USART_INT_FLAG_RBNE);
        // 接收数据
        uint8_t data = usart_data_receive(USART_PORT);
        // usart_sendByte(USART_PORT, data); // 回显数据

        if (RX_Count >= RX_LENGTH)
            RX_Count = 0; // 越界处理
        RX_Buffer[RX_Count] = data;
        RX_Count++;
    }

    // IDLE, 空闲中断标志位自动置1手动置0
    if (SET == usart_interrupt_flag_get(USART_PORT, USART_INT_FLAG_IDLE)) {
        // 清除标志位, 可以让中断重复触发(接收一次达到清除标志位的作用)
        usart_data_receive(USART_PORT); // 接收的数据不用，用于清除标志位

        RX_Buffer[RX_Count] = '\0'; // 字符串结束符

#if USART_RX_CB_SW
        USART_RX_Recall(RX_Buffer, RX_Count);
#endif
        RX_Count = 0; // 重置计数器
    }
#endif
}